/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.ColSubtypeRev;
import ro.dta.idbi.ui.sql.SqlColumnCell;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;

/**
 * UI for the reverse transformation of the <i>column to subtype pattern</i>
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColSubtypeRevDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 3442164639547443540L;
	private static final Logger LOGGER = Logger
			.getLogger("ro.dta.idbi.ui.patterns.ColSubtypeRevDialog");
	
	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length != 2) {
			return false;
		}

		// Both items selected have to be tables
		if (!(objects[0] instanceof ISqlTable) || !(objects[1] instanceof ISqlTable)) {
			return false;
		}

		ISqlTable table1 = (ISqlTable) objects[0];
		ISqlTable table2 = (ISqlTable) objects[1];
		ISqlTable child;
		List<String> t1Super = table1.getSupertypesNames();
		List<String> t2Super = table2.getSupertypesNames();

		if (t1Super.contains(table2.getValue().toString())) {
			child = table1;
		} else if (t2Super.contains(table1.getValue().toString())) {
			child = table2;
		} else {
			// No relation between the two tables
			return false;
		}

		// Child table must contain exactly one column that is not part of the
		// PK and is mandatory
		List<ISqlColumn> cols = child.getColumns();
		int validCols = 0;
		for (ISqlColumn c : cols) {
			if (!c.isPK()) {
				if (c.isNullable()) {
					return false;
				} else {
					validCols++;
				}
			}
		}

		if (validCols != 1) {
			return false;
		}

		return true;
	}
	
	/**
	 * Parent table
	 */
	private ISqlTable parent;

	/**
	 * Child tables
	 */
	private ISqlTable child;

	/**
	 * Column being moved to the supertype
	 */
	private ISqlColumn childCol;

	/**
	 * Revoerse of the transformation <i>column to subtype</i> dialog class
	 * constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public ColSubtypeRevDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given constructs.");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.AbstractTransDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		initialise();
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			final SqlTableCell newParent = (SqlTableCell) parent.clone();
			newParent.layout(graph);
			final SqlTableCell newChild = (SqlTableCell) child.clone();
			newChild.setStyle("SEL_TABLE");
			newChild.layout(graph);

			SqlForeignKeyCell foreignKey = new SqlForeignKeyCell(null, null, "", "", newChild, newParent,
					"FK_EDGE");
			graph.addCell(foreignKey, graph.getDefaultParent());
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractTransDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			final SqlTableCell newParent = (SqlTableCell) parent.clone();
			final SqlColumnCell newCol = (SqlColumnCell) childCol.clone();
			newCol.setNullable(true);
			newCol.setValue(newCol.getValue() + "?");
			newParent.setGeometry(new mxGeometry(5, 5, parent.getGeometry().getWidth(), parent
					.getGeometry().getHeight()));
			newParent.addColumn(newCol);
			newParent.relayout(graph);
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/**
	 * Initialise macro field
	 */
	private void initialise() {
		ISqlTable table1 = (ISqlTable) element[0];
		ISqlTable table2 = (ISqlTable) element[1];
		List<String> t1Super = table1.getSupertypesNames();

		if (t1Super.contains(table2.getValue().toString())) {
			parent = table2;
			child = table1;
		} else {
			parent = table1;
			child = table2;
		}

		// Child table must contain exactly one column that is not part of the
		// PK and is mandatory
		List<ISqlColumn> cols = child.getColumns();
		for (ISqlColumn c : cols) {
			if (!c.isPK()) {
				childCol = c;
				break;
			}
		}

		macroField.setText(ColSubtypeRev.COMMAND + " (" + "<<" + child.getValue() + ","
				+ childCol.getValue() + ">>, <<" + parent.getValue() + ">>)");
	}
}
